package com.billow.net.rtmp
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.net.ObjectEncoding;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.IDataInput;
	import flash.utils.Timer;
	
	internal class RtmpDecoder extends EventDispatcher
	{
		private const READED_FIRSTBYTE:String = "readed_firstByte";
		private const READED_HEADER:String = "readed_header";
		private const READED_BODY:String = "readed_body";
		
		private var _packets:Dictionary;
		private var _readStatus:String;
		
		private var _rtmpHeadSize:uint;
		private var _rtmpChannel:uint;
		
		private var _packet:RtmpPacket;
		
		public function RtmpDecoder()
		{
			_packets = new Dictionary();
			_readStatus = READED_BODY;
			_rtmpHeadSize = 0;
			_rtmpChannel = 0;
		}
		
		public function get packet():RtmpPacket {return _packet;}
		
		public function process(dataInput:IDataInput, encoding:uint):void
		{
			while(true)
			{
				if(_readStatus == READED_BODY)
					if(!readFirst(dataInput, encoding)) break;
				
				if(_readStatus == READED_FIRSTBYTE)
					if(!readHead(dataInput)) break;
				
				if(_readStatus == READED_HEADER)
					if(!readBody(dataInput)) break;
			}
		}
		
		private function readFirst(dataInput:IDataInput, encoding:uint):Boolean
		{
			if(dataInput.bytesAvailable > 0)
			{
				var first:uint = dataInput.readUnsignedByte();
				_rtmpHeadSize = first & 0xC0;
				_rtmpChannel = first & 0x3F;
				
				if(_packets[_rtmpChannel] == null)
				{
					_packets[_rtmpChannel] = new RtmpPacket();
					_packets[_rtmpChannel].rtmpChannel = _rtmpChannel;
				}
				
				var packet:RtmpPacket = _packets[_rtmpChannel];
				packet.objectEncoding = encoding;
				
				switch(_rtmpHeadSize)
				{
					case 0xC0:	_rtmpHeadSize = 1;	break;
					case 0x80:	_rtmpHeadSize = 4;	break;
					case 0x40:	_rtmpHeadSize = 8;	break;
					case 0x00:	_rtmpHeadSize = 12;	break;
					default: 	trace("RTMP解码包错误(decode packet error)!"); break;
				}
				
				_readStatus = READED_FIRSTBYTE;
			}else{
				return false;
			}
			return true;
		}
		
		private function readHead(dataInput:IDataInput):Boolean
		{
			var packet:RtmpPacket = _packets[_rtmpChannel];
			
			if(dataInput.bytesAvailable >= _rtmpHeadSize - 1)
			{
				if(_rtmpHeadSize > 1)
				{
					packet.streamTimeStamp = (dataInput.readUnsignedShort() << 8) | dataInput.readUnsignedByte();
					
					if(_rtmpHeadSize > 4)
					{
						packet.rtmpBodySize = (dataInput.readUnsignedShort() << 8) | dataInput.readUnsignedByte();
						packet.rtmpBodyType = dataInput.readUnsignedByte();
						
						packet.chunkSize = (packet.rtmpBodyType == 0x08 ? 65 : 128);
						packet.remaining = packet.rtmpBodySize;
						
						if(_rtmpHeadSize > 8)
						{
							packet.streamChannel = (dataInput.readUnsignedByte() & 0xFF) | (dataInput.readUnsignedByte() << 8) |
													(dataInput.readUnsignedByte() << 16) | (dataInput.readUnsignedByte() << 24);
						}
					}
				}
				
				if(packet.chunkSize > packet.remaining) packet.chunkSize = packet.remaining;
				
				_readStatus = READED_HEADER;
			}else {
				return false;
			}
			return true;
		}
		
		private function readBody(dataInput:IDataInput):Boolean
		{
			var packet:RtmpPacket = _packets[_rtmpChannel];
			
			if(dataInput.bytesAvailable >= packet.chunkSize)
			{
				dataInput.readBytes(packet.rtmpBody, packet.rtmpBody.position, packet.chunkSize);
				packet.rtmpBody.position += packet.chunkSize;
				packet.remaining -= packet.chunkSize;
				
				_readStatus = READED_BODY;
				
				if(packet.remaining == 0)
				{
					packet.rtmpBody.position = 0;
					
					_packet = packet.clone();
					dispatchEvent(new Event("decoder_complete"));
					
					packet.rtmpBody.clear();
					//packet.chunkSize = 128;
					packet.chunkSize = (packet.rtmpBodyType == 0x08 ? 65 : 128);
					packet.remaining = packet.rtmpBodySize;
				}
			}else {
				return false;
			}
			return true;
		}
	}
}